Набор Docker-приложений для проверки сканеров безопасности и pen-test инструментов: разнородные сервисы, которые сканер может обнаружить, классифицировать и (в demo-файлах) проэксплуатировать. Окружение для разработки и демонстрации, не продакшн-деплой.
Файлы — независимые compose-оверлеи, поднимаются поверх базового compose.yml
(кроме vuln-demo.yml и vuln-demo-glpi.yml, которые самодостаточны):
# все команды — из корня этого репозитория
# база: nginx + gitea + postgres + confluence
docker compose -f compose.yml up -d
# + honeypot: разнородные сервисы на одном хосте
docker compose -f compose.yml -f honeypot.yml up -d
# уязвимый Roundcube (CVE-2025-49113) — отдельно, самодостаточен
docker compose -f vuln-demo.yml up -d
# уязвимый GLPI (CVE-2022-35914) — отдельно, самодостаточен, поднимается ~1-2 минуты (установка БД)
docker compose -f vuln-demo-glpi.yml up -dnginx + gitea + postgres + confluence. Версии актуальные (не уязвимые) —
сервисы нужны для проверки discovery и классификации продуктов, а не для поиска
реальных CVE.
4 сервиса-приманки (ftp/nntp/irc/vnc баннеры) на одном хосте — по разнородности сервисов сканер должен распознать хост как honeypot.
roundcube/roundcubemail:1.6.10-apache — последний релиз перед фиксом в
1.6.11. В отличие от compose.yml, это реально уязвимый сервис (Roundcube
RCE, CVSS 9.9, CISA KEV): сканер с соответствующей nuclei-проверкой должен найти
её по-настоящему — через классификацию product: "roundcube" (title
логин-страницы) и неавторизованный version-fingerprint внутри nuclei-шаблона
(подробнее — в шапке самого vuln-demo.yml).
darkvills/glpi:10.0.2 + mysql:8.4.10. Как и Roundcube, это реально
уязвимый сервис (GLPI RCE через тестовый файл библиотеки htmlawed, CVSS 9.8,
CISA KEV): уязвимость эксплуатируется тем же nuclei-шаблоном, который её находит
(POST с hhook=exec возвращает содержимое /etc/passwd).
GLPI — система учёта IT-парка (ITAM) и service desk.
БД здесь не для эксплойта (уязвимый файл — отдельный vendor-скрипт вне
роутинга GLPI и отдаётся независимо от состояния установки), а для
классификации: без config_db.php GLPI на любой странице отдаёт заглушку
"Missing configuration" — ни title, ни nuclei-шаблон glpi-status-page не
сработают, и продукт не распознаётся. Поэтому glpi в этом файле
зависит от db (healthcheck) и при первом старте сам прогоняет
glpi:database:install перед запуском Apache (подробности — в шапке
самого vuln-demo-glpi.yml).
Официальный образ glpi/glpi на Docker Hub хранит только пропатченные
версии, а сборка версии 10.0.2 из официального Dockerfile ломается —
composer.lock ссылается на нестабильный сторонний сайт
(bioinformatics.org, отдаёт 503). Поэтому здесь используется готовый
образ стороннего автора (darkvills/glpi:10.0.2) — это заведомо
жертвенный контейнер (весь смысл — чтобы его взломала наша же проверка),
без сборки и без сетевых зависимостей при каждом поднятии.
Оба демо (vuln-demo.yml и vuln-demo-glpi.yml) рассчитаны на запуск
сканера с отдельной машины — target виден как обычный сосед по LAN на
опубликованном порту. Ни один из файлов не использует трюки с
network_mode: bridge для обхода WiFi hairpin (см. ниже) — раньше он был
в vuln-demo.yml, но для реального сценария (сканер и target на разных
машинах) он не нужен: обход имел смысл только когда сканер и target подняты
на одном хосте.
Если сканер и demo-контейнер подняты на одной Linux-машине, и эта машина смотрит в сеть через WiFi-адаптер, скан может не найти сервис вообще — не из-за бага в сканере или каталоге проверок, а из-за особенности самого WiFi.
Условие узкое — срабатывает только при одновременном совпадении всех трёх: нативный Linux (не гостевая ОС) + WiFi + сканер и target на одной машине. Во всех остальных случаях всё в порядке:
| Хост сканера | WiFi | Ethernet |
|---|---|---|
| Native Linux (Docker прямо на хосте) | ✅ ок | |
| VM любой ОС (Linux/Windows/macOS-гость) | ✅ ок | ✅ ок |
| macOS/Windows + Docker Desktop | ✅ ок | ✅ ок |
| сканер и target на разных машинах | ✅ ок (не применимо) | ✅ ок (не применимо) |
Docker Desktop на macOS/Windows тоже попадает в "VM"-строку, даже если сам
ноутбук на WiFi: Docker Desktop сам работает внутри Linux VM (см. ниже), так
что nmap там видит виртуальный адаптер, а не настоящий WiFi-интерфейс —
hairpin специфичен именно для Linux-хоста, где Docker установлен нативно.
Механика: discovery-шаг (nmap) сканер запускает с network_mode: host —
это осознанный выбор, иначе на Linux он не увидит ни реальную LAN, ни другие
docker-сети (в отличие от macOS/Windows, где Docker сам работает внутри Linux
VM). Хост, обращаясь к своему же внешнему IP на
docker-опубликованный порт (docker run -p 8081:80 ... → сканируем
<свой-WiFi-IP>:8081), гоняет пакет туда-обратно через один и тот же
физический интерфейс (hairpin). На Ethernet это обычно штатно
NAT'ится обратно. На WiFi — часто нет: пакет уходит на точку доступа и не
возвращается тем же путём (rp_filter/reverse-path защита ядра расценивает
такой ответ как "марсианский" и дропает, либо сама точка доступа не
поддерживает client-to-client hairpin). Итог: nmap видит порт закрытым,
хотя контейнер реально отвечает.
Проверить эмпирически (замените IP/порт на свои):
# видит порт (обычный контейнер на bridge, без hairpin через WiFi):
docker run --rm --network host --cap-add NET_RAW instrumentisto/nmap:7.98 \
-sV -Pn --open -oX - <IP контейнера из `docker inspect`>
# не видит (hairpin через WiFi-IP хоста):
docker run --rm --network host --cap-add NET_RAW instrumentisto/nmap:7.98 \
-sV -Pn --open -oX - <WiFi IP хоста>Специального обхода в demo-файлах сейчас нет — простейший способ избежать проблемы: гонять сканер и target на разных машинах (как и задумано), либо через Ethernet, либо в VM.
Почему это не всплывает в VM (например, Windows-хост с Debian-гостем): гостевая ОС видит виртуальный сетевой адаптер (virtio-net/e1000/vmxnet3), который для её ядра — обычный Ethernet-подобный интерфейс, независимо от того, как физически подключён к сети хост-гипервизор. Hairpin внутри VM разруливает виртуальный свитч гипервизора, а не настоящая 802.11-передача — проблема просто не воспроизводится.